package factorytest.iflytek.com.einkiflytekfactorytest.engineerui;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Set;


import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import factorytest.iflytek.com.einkiflytekfactorytest.R;
import factorytest.iflytek.com.einkiflytekfactorytest.ViewDialogFragment;
import factorytest.iflytek.com.einkiflytekfactorytest.common.PermissionManager;
import factorytest.iflytek.com.einkiflytekfactorytest.common.SettingsUtils;
import factorytest.iflytek.com.einkiflytekfactorytest.utils.DebugLog;
import factorytest.iflytek.com.einkiflytekfactorytest.common.FTUtils;
import factorytest.iflytek.com.einkiflytekfactorytest.utils.ReflectUtils;

import static android.bluetooth.BluetoothDevice.BOND_BONDED;

public class BTActivity extends Activity implements View.OnClickListener {

    private final String TAG = "BTActivity";

    private Context mContext;
    private TextView txtInfo;
    private Button btnFinish;

    private boolean mustFailedWithoutTestSco = true;

    private TextView txtBluetoothScoInfo;
    private TextView txtBluetoothScoState;
    private Button btnTestBluetoothSco;
    private boolean isTestBluetoothScoStarted = false;

    private Button btnJumpToSelectBT;

    private boolean isPcba;

    /**
     * 是否是讯飞毛旺搞的新接口
     */
    private boolean mUseNewInterface = false;
    public static final int FORCE_NONE = 0;
    public static final int FORCE_SPEAKER = 1;
    public static final int FORCE_HEADPHONES = 2;
    public static final int FORCE_BT_SCO = 3;
    public static final int FORCE_BT_A2DP = 4;
    public static final int FORCE_WIRED_ACCESSORY = 5;
    public static final int FORCE_MIC = 15;
    public static final int FOR_INPUT = 7;
    public static final int FOR_OUTPUT = 8;

    private static final int MODE_MIC = 0;
    private static final int MODE_HEADSET = 1;
    private static final int MODE_BLUETOOTH = 2;
    private static final int MODE_SPEAKER = 0;
    private static final int MODE_NONE = 3;



    private AudioManager mAudioManager;
    private MyHandler myHandler;
    private boolean isScoConnected = false;

    private int scoReconnectedCount = 0;
    private final int MAX_scoReconnectedCount = 5;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //保持屏幕常亮
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_bt);
        mContext = this;

        myHandler = new MyHandler(this);

        isPcba = getIntent().getBooleanExtra("isPcba", false);

        txtInfo = (TextView) findViewById(R.id.txtInfo);
        btnFinish = (Button) findViewById(R.id.btnFinish);
        txtBluetoothScoInfo = (TextView) findViewById(R.id.txtBluetoothScoInfo);
        txtBluetoothScoState = (TextView) findViewById(R.id.txtBluetoothScoState);
        btnTestBluetoothSco = (Button) findViewById(R.id.btnTestBluetoothSco);
        btnJumpToSelectBT = (Button) findViewById(R.id.btnJumpToSelectBT);

        btnTestBluetoothSco.setOnClickListener(this);
        btnFinish.setOnClickListener(this);
        btnJumpToSelectBT.setOnClickListener(this);

        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        mUseNewInterface = ReflectUtils.getMethodWithNoException(mAudioManager, "stopBTScoAndResetInput") != null;

        DebugLog.e("mUseNewInterface = " + mUseNewInterface);

        initPermission();
    }

    private void tryJumpBTSettings() {
        if (isPcba) {
            //跳转到蓝牙连接页面
            startBTSettings();

        } else {

            if (!isBTBonded()) {

                startBTSettings();

            }
        }
    }


    private void initPermission() {
        //如果无权限就申请然后回调，如果有权限就执行录音监听
        if (!isRecordGotPermission()) {

            //同时申请多个权限
            PermissionManager.getInstance(getApplicationContext()).execute(this,
                    android.Manifest.permission.RECORD_AUDIO,
                    android.Manifest.permission.WRITE_SETTINGS,
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE);

        } else {

            tryJumpBTSettings();

        }

    }

    private boolean isRecordGotPermission() {

        return (ContextCompat.checkSelfPermission(mContext, android.Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
                && ContextCompat.checkSelfPermission(mContext, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
                && ContextCompat.checkSelfPermission(mContext, android.Manifest.permission.WRITE_SETTINGS) == PackageManager.PERMISSION_GRANTED
        );

    }

    //如果有权限的话返回
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        PermissionManager.getInstance(getApplicationContext()).
                onRequestPermissionsResult(requestCode, permissions, grantResults);

        DebugLog.e(" ~~~~~~~~~~~~~~onRequestPermissionsResult");

        for (String per : permissions) {
            DebugLog.e("per = " + per);
        }

        for (int res : grantResults) {
            DebugLog.e("res = " + res);
        }

        tryJumpBTSettings();

    }

    private void disableBT() {
        BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
        if (btAdapter != null && btAdapter.isEnabled()) {
            btAdapter.disable();
        }
    }

    private void showFinishDialog(int titleRes, int msgRes) {
        final String title = getString(titleRes);
        final String msg = getString(msgRes);
        ViewDialogFragment dialog = ViewDialogFragment.newInstance(title, msg);

        dialog.show(getFragmentManager(),
                new ViewDialogFragment.Callback() {
                    @Override
                    public void onClick() {

                        finishWithResult(true);

                    }
                },
                new ViewDialogFragment.Callback() {
                    @Override
                    public void onClick() {

                        finishWithResult(false);
                    }
                });
    }

    private void showDialog() {

        showFinishDialog(R.string.bt_test, R.string.item_exit_test);

    }

    private void finishWithResult(boolean ok) {
        int resultCode = RESULT_CANCELED;
        int resId = R.string.test_fail;
        if (ok) {
            resultCode = RESULT_OK;
            resId = R.string.test_ok;
        }

        Toast.makeText(mContext, resId, Toast.LENGTH_SHORT).show();
        setResult(resultCode);
        ActivityCompat.finishAfterTransition(BTActivity.this);

    }

    private void startBTSettings() {

        if (startAndroidBluetoothSettings()) {
            return;
        }

        if (startIflytekBluetoothSettings()) {
            return;
        }

        Toast.makeText(mContext, R.string.bt_no_settings, Toast.LENGTH_LONG).show();

    }

    private boolean startAndroidBluetoothSettings() {

        try {

            Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
            startActivity(intent);

        } catch (Exception e) {

            return false;
        }

        return true;
    }

    private boolean startIflytekBluetoothSettings() {

        try {

            Intent intent = new Intent(Intent.ACTION_MAIN);
            ComponentName cn = new ComponentName("com.iflytek.ainote.settings", "com.iflytek.ainote.settings.ui.activity.BlueToothActivity");
            intent.setComponent(cn);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);

        } catch (Exception e) {

            return false;
        }

        return true;

    }

    private boolean isBTBonded() {

        BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
        if (btAdapter == null) {
            return false;
        }

        if (!btAdapter.isEnabled()) {
            return false;
        }

        txtInfo.setText(getString(R.string.bt_test_not_bonded));

        Set<BluetoothDevice> deviceList = btAdapter.getBondedDevices();
        for (BluetoothDevice device : deviceList) {
            if (device.getBondState() == BOND_BONDED) {
                txtInfo.setText(getResources().getString(R.string.bt_test_bonded, device.getName() + "(" + device.getAddress() + ")"));
                return true;
            }
        }
        return false;
    }

    private String getBondName() {

        BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
        if (btAdapter == null) {
            return "";
        }

        Set<BluetoothDevice> deviceList = btAdapter.getBondedDevices();
        for (BluetoothDevice device : deviceList) {
            if (device.getBondState() == BOND_BONDED) {
                return device.getName() + "(" + device.getAddress() + ")";
            }
        }
        return "";
    }

    @Override
    protected void onResume() {
        super.onResume();

        DebugLog.e("onResume");

        if (isRecordGotPermission()) {
            registerBTReceiver();
            registerBluetoothSCOReceiver();
        }

        if (!isPcba) {


            mustFailedWithoutTestSco = true;

            if (!isBTBonded()) {

                txtBluetoothScoInfo.setVisibility(View.INVISIBLE);
                txtBluetoothScoState.setVisibility(View.INVISIBLE);
                btnTestBluetoothSco.setVisibility(View.INVISIBLE);

            } else {

                txtBluetoothScoInfo.setVisibility(View.VISIBLE);
                txtBluetoothScoState.setVisibility(View.VISIBLE);
                btnTestBluetoothSco.setVisibility(View.VISIBLE);

                if (isRecordGotPermission()) {
                    startRecorderScoMode();
                    btnTestBluetoothSco.setText(R.string.bt_test_sco_stop);
                    isTestBluetoothScoStarted = true;
                }
            }

        } else {

            txtBluetoothScoInfo.setVisibility(View.INVISIBLE);
            txtBluetoothScoState.setVisibility(View.INVISIBLE);
            btnTestBluetoothSco.setVisibility(View.INVISIBLE);


            if (isBTBonded()) {
                Toast.makeText(mContext, R.string.test_ok, Toast.LENGTH_SHORT).show();
                setResult(RESULT_OK);
            } else {
                Toast.makeText(mContext, R.string.test_fail, Toast.LENGTH_SHORT).show();
                setResult(RESULT_CANCELED);
            }

            ActivityCompat.finishAfterTransition(BTActivity.this);

        }

    }

    @Override
    protected void onPause() {
        super.onPause();

        if (isRecordGotPermission()) {
            unregisterBTReceiver();
            unregisterBluetoothSCOReceiver();
            stopRecorderScoMode();
        }

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    public void onClick(View v) {

        if (v == btnFinish) {
            if (!isPcba) {

                if (mustFailedWithoutTestSco) {

                    finishWithResult(false);

                } else {

                    showDialog();
                }

            } else {

                disableBT();

                showDialog();

            }
        } else if (v == btnTestBluetoothSco) {

            if (isTestBluetoothScoStarted) {
                isTestBluetoothScoStarted = false;
                btnTestBluetoothSco.setText(R.string.bt_test_sco_start);
                stopRecorderScoMode();
            } else {

                isTestBluetoothScoStarted = true;
                btnTestBluetoothSco.setText(R.string.bt_test_sco_stop);
                startRecorderScoMode();
            }

        } else if (v == btnJumpToSelectBT) {
            startBTSettings();
        }

    }

    private int setForceUse(int usage, boolean record) {

        int config = FORCE_NONE;
        switch (usage) {
            case MODE_MIC | MODE_SPEAKER:
                config = record ? FORCE_MIC : FORCE_NONE;
                break;
            case MODE_BLUETOOTH:
                config = record ? FORCE_BT_SCO : FORCE_BT_A2DP;
                break;
            case MODE_HEADSET:
                config = record ? FORCE_WIRED_ACCESSORY : FORCE_HEADPHONES;
                break;
            default:
                break;
        }
        usage = record ? FOR_INPUT : FOR_OUTPUT;
        DebugLog.e("setForceUse usage= " + usage + " config=" + config);
        Object obj = ReflectUtils.invokeMethodWithNoException("android.media.AudioSystem", "setForceUse", usage, config);
        DebugLog.e("setForceUse obj = " + obj);
        if (obj instanceof Integer) {
            return (int) obj;
        }
        return -1;
    }


    private void registerBTReceiver() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        registerReceiver(btReceiver, filter);
    }

    private void unregisterBTReceiver() {
        unregisterReceiver(btReceiver);
    }


    private BroadcastReceiver btReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (action == null) {
                return;
            }
            DebugLog.i("action:" + action);
            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
                switch (state) {
                    case BluetoothAdapter.STATE_OFF:
                        Log.d(TAG, "STATE_OFF 手机蓝牙关闭");
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:
                        Log.d(TAG, "STATE_TURNING_OFF 手机蓝牙正在关闭");
                        break;
                    case BluetoothAdapter.STATE_ON:
                        Log.d(TAG, "STATE_ON 手机蓝牙开启");
                        break;
                    case BluetoothAdapter.STATE_TURNING_ON:
                        Log.d(TAG, "STATE_TURNING_ON 手机蓝牙正在开启");
                        break;
                }
            } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {

                if (isBTBonded()) {
                    DebugLog.w("蓝牙配对成功.");
                }

//                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//                String name = device.getName();
//                Log.d(TAG, "device name: " + name);
//                int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
//                switch (state) {
//                    case BluetoothDevice.BOND_NONE:
//                        Log.d(TAG, "BOND_NONE 删除配对");
//                        break;
//                    case BluetoothDevice.BOND_BONDING:
//                        Log.d(TAG, "BOND_BONDING 正在配对");
//                        break;
//                    case BluetoothDevice.BOND_BONDED:
//                        Log.d(TAG, "BOND_BONDED 配对成功");
//                        break;
//                }

            }

        }
    };

    ///////////////////////////////////////////////////////////////////////////////////

    private void startRecorderScoMode() {

        stopRecorderScoMode();

        DebugLog.e("startRecorderScoMode....................");

        String state = getString(R.string.bt_test_sco_reconnect_info, scoReconnectedCount++);
        txtBluetoothScoState.setText(state);

        if (scoReconnectedCount > MAX_scoReconnectedCount) {

            Toast.makeText(mContext, R.string.bt_test_sco_connected_failed, Toast.LENGTH_SHORT).show();
            setResult(RESULT_CANCELED);

            ActivityCompat.finishAfterTransition(BTActivity.this);
            return;
        }

        if (mUseNewInterface && isRecordGotPermission()) {
            SettingsUtils.setBluetoothScoConnected(mContext, true);
            myHandler.sendEmptyMessage(MyHandler.MSG_SCO_CONNECTED);
        } else {
            mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
            mAudioManager.setBluetoothScoOn(true);
            mAudioManager.startBluetoothSco();
            mAudioManager.setMicrophoneMute(false);
        }

    }

    private void stopRecorderScoMode() {
        DebugLog.e("stopRecorderScoMode....................");

        isScoConnected = false;

        if (mAudioManager != null) {
            mAudioManager.setMode(AudioManager.MODE_NORMAL);
            mAudioManager.stopBluetoothSco();
            mAudioManager.setBluetoothScoOn(false);

        }

        if (mUseNewInterface && isRecordGotPermission()) {
            SettingsUtils.setBluetoothScoConnected(mContext, false);
        }

        stopRecordAndPlay();
    }

    private void btScoConnected() {


        myHandler.sendEmptyMessageDelayed(MyHandler.MSG_FINISH_ENABLED, MyHandler.DELAY_FINISH_ENABLED);

        txtBluetoothScoState.setText(R.string.bt_test_sco_connected_ok);

        //开始录音和放音.
        startRecordAndPlay();
    }

    private void registerBluetoothSCOReceiver() {
        IntentFilter filter = new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
        registerReceiver(mBluetoothSCOReceiver, filter);
    }

    private void unregisterBluetoothSCOReceiver() {
        unregisterReceiver(mBluetoothSCOReceiver);
    }

    private BroadcastReceiver mBluetoothSCOReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            DebugLog.e("action: " + action);

            Bundle bundle = intent.getExtras();
            if (bundle != null && !bundle.isEmpty()) {
                DebugLog.e("bundle: " + bundle);
            }

            if (isScoConnected || mUseNewInterface) {
                return;
            }

            if (AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED.equals(action)) {
                int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_ERROR);
                DebugLog.e("state = " + state);
                if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) {

                    isScoConnected = true;

                    myHandler.sendEmptyMessage(MyHandler.MSG_SCO_CONNECTED);

                } else {

                    myHandler.sendEmptyMessageDelayed(MyHandler.MSG_SOC_TRY_RECONNECTED, MyHandler.DELAY_SCO_TRY_RECONNECTED);
                }
            }
        }
    };


    private static final class MyHandler extends Handler {

        private final static int MSG_SCO_CONNECTED = 100;
        private final static int MSG_SOC_TRY_RECONNECTED = 200;
        private final static int DELAY_SCO_TRY_RECONNECTED = 1000;

        private final static int MSG_FINISH_ENABLED = 1000;
        private final static int DELAY_FINISH_ENABLED = 5 * 1000;//至少测试5秒


        private final WeakReference<BTActivity> reference;

        public MyHandler(BTActivity activity) {
            reference = new WeakReference<BTActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {

            BTActivity activity = reference.get();
            if (activity == null) {
                return;
            }

            switch (msg.what) {

                case MSG_SCO_CONNECTED:
                    activity.btScoConnected();
                    break;
                case MSG_SOC_TRY_RECONNECTED:
                    activity.startRecorderScoMode();
                    break;
                case MSG_FINISH_ENABLED:
                    activity.btnFinish.setEnabled(true);
                    activity.mustFailedWithoutTestSco = false;
                    break;

            }

        }
    }

    ///////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////

    private RecordAndPlayThread recordAndPlayThread = null;


    private void startRecordAndPlay() {

        DebugLog.e("startRecordAndPlay");
        setForceUse(MODE_BLUETOOTH, true);
        mAudioManager.setMode(AudioManager.MODE_IN_CALL);

        if (recordAndPlayThread == null) {
            recordAndPlayThread = new RecordAndPlayThread("bt_sco", mContext, mUseNewInterface);
        }

        recordAndPlayThread.start();

    }

    private void stopRecordAndPlay() {

        DebugLog.e("stopRecordAndPlay");


        if (recordAndPlayThread != null) {
            recordAndPlayThread.joinThread();
            recordAndPlayThread = null;
        }

    }


    private static final class RecordAndPlayThread extends Thread {

        private static final String RECORDER_FILE_PATH = FTUtils.getFTStorageDirectory() + File.separator + "sco_test.pcm";

        private volatile boolean keepAlive = true;

        private  int SAMPLE_RATE_IN_HZ = 16000;

        private AudioRecord mAudioRecord;
        private AudioTrack mAudioTrack;

        private final boolean mUseNewInterface;
        private final Context mContext;

        public RecordAndPlayThread(String name, Context ctx, boolean useNewInterface) {
            super(name);
            mUseNewInterface = useNewInterface;
            mContext = ctx;
        }
        @Override
        public void run() {

            BufferedOutputStream bos = null;

            try {

                int sampleRate = SAMPLE_RATE_IN_HZ;

                int bufferSizeRec = AudioRecord.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT) * 2;
                if (mAudioRecord == null) {
                    mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSizeRec);
                }
                if (mUseNewInterface) {
                    ReflectUtils.invokeMethodWithNoException(mAudioRecord, "startRecording", mContext);
                    DebugLog.e("here to startRecording(ctx)");
                } else {
                    mAudioRecord.startRecording();
                }

                int bufferSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT) * 2;

                if (mAudioTrack == null) {
                    mAudioTrack = new AudioTrack(/*AudioManager.STREAM_BLUETOOTH_SCO*/6, sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
                } else {
                    mAudioTrack.stop();
                }

                mAudioTrack.play();

                File file = new File(RECORDER_FILE_PATH);
                if (file.exists()) {
                    file.delete();
                }
                bos = new BufferedOutputStream(new FileOutputStream(file));

                byte[] buffer = new byte[Math.max(bufferSize, bufferSizeRec)];

                while (keepAlive) {

                    int len = mAudioRecord.read(buffer, 0, buffer.length);

                    if (len < AudioRecord.SUCCESS) {
                        DebugLog.e("recordAndPlay has error--------------");
                        break;
                    }
                    mAudioTrack.write(buffer, 0, len);

                    bos.write(buffer, 0, len);
                    bos.flush();
                }

            }catch (Exception e) {

                DebugLog.e(e.getMessage());

            } finally {
                DebugLog.e("recordAndPlay finish..........................");

                if (bos != null) {
                    try {
                        bos.close();
                    } catch (IOException ignored) {
                    }
                }

                if (mAudioRecord != null) {
                    try {
                        mAudioRecord.stop();
                        mAudioRecord.release();
                        mAudioRecord = null;
                    } catch (Exception ignored) {
                    }
                }

                if (mAudioTrack != null) {
                    try {
                        mAudioTrack.stop();
                        mAudioTrack.flush();
                        mAudioTrack.release();
                        mAudioTrack = null;
                    } catch (Exception ignored) {
                    }
                }
            }

        }

        public void joinThread() {
            keepAlive = false;
            while (isAlive()) {
                try {
                    join();
                } catch (InterruptedException e) {
                    // Ignore.
                }
            }
        }

    }

}
